package git.soulbgm.common.socket;

import java.util.Date;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import git.soulbgm.common.enums.Status;
import git.soulbgm.pojo.*;
import git.soulbgm.pojo.dto.*;
import git.soulbgm.service.*;
import git.soulbgm.utils.JsonTool;
import git.soulbgm.utils.LogHelper;
import git.soulbgm.utils.StringUtil;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.List;

import static git.soulbgm.common.MonitorConstants.*;

/**
 * 监控数据处理
 *
 * @author SoulBGM
 * @date 2024-04-08
 */
@Component
public class MonitorDataHandler extends SimpleChannelInboundHandler<DatagramPacket> {

    private static final LogHelper LOG = new LogHelper(MonitorDataHandler.class);
    private final NodeInfoService nodeInfoService;
    private final NodeStatusService nodeStatusService;
    private final NodeUsedInfoService nodeUsedInfoService;
    private final ServiceInfoService serviceInfoService;
    private final ServiceStatusService serviceStatusService;
    private final SoftwareInfoService softwareInfoService;
    private final SoftwareStatusService softwareStatusService;

    public MonitorDataHandler(NodeInfoService nodeInfoService, NodeStatusService nodeStatusService, NodeUsedInfoService nodeUsedInfoService, ServiceInfoService serviceInfoService, ServiceStatusService serviceStatusService, SoftwareInfoService softwareInfoService, SoftwareStatusService softwareStatusService) {
        this.nodeInfoService = nodeInfoService;
        this.nodeStatusService = nodeStatusService;
        this.nodeUsedInfoService = nodeUsedInfoService;
        this.serviceInfoService = serviceInfoService;
        this.serviceStatusService = serviceStatusService;
        this.softwareInfoService = softwareInfoService;
        this.softwareStatusService = softwareStatusService;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) {
        byte[] data = ByteBufUtil.getBytes(msg.content());
        String json = new String(data, StandardCharsets.UTF_8);
        MonitorData monitorData = JsonTool.parse(json, MonitorData.class);
        Server server = monitorData.getServer();
        Network network = server.getNetwork();
        String ip = network.getIp();
        NodeInfo nodeInfo = resetNodeStatus(server, ip);
        addNodeUsedInfo(nodeInfo, server);
        resetServiceStatus(monitorData.getService(), nodeInfo.getNodeCode(), ip);
        resetSoftwareStatus(monitorData.getSoftware(), nodeInfo.getNodeCode(), ip);
    }

    private NodeInfo resetNodeStatus(Server server, String ip) {
        NodeInfo info = nodeInfoService.getOne(new LambdaQueryWrapper<NodeInfo>().eq(NodeInfo::getIp, ip));
        if (info == null) {
            Sys sys = server.getSys();
            String hostname = sys.getComputerName();
            info = new NodeInfo();
            info.setNodeCode(info.getNodeCode());
            info.setHostname(hostname);
            info.setIp(ip);
            info.setPort(MONITOR_AGENT_DEFAULT_PORT);
            String osName = sys.getOsName();
            info.setOperatingSystem("linux".equalsIgnoreCase(osName) ? SYSTEM_LINUX : SYSTEM_WINDOWS);
            info.setSshPort(SSH_DEFAULT_PORT);
            info.setUsername(SSH_DEFAULT_USERNAME);
            info.setPassword(SSH_DEFAULT_PASSWORD);
            String userDir = sys.getUserDir();
            info.setStartupScriptPath(userDir + STARTUP_SCRIPT_DEFAULT_PATH);
            info.setStopScriptPath(userDir + STOP_SCRIPT_DEFAULT_PATH);
            info.setRegionCode(REGION_DEFAULT_CODE);
            info.setMonitorMode(MONITOR_MODE_ACTIVE);
            info.setSelfStart(Status.NO.code);
            info.setStatus(Status.YES.code);
            info.setRemark(StringUtil.format("{}({})主动发送监控数据", hostname, ip));
            boolean result = nodeInfoService.save(info);
            LOG.info("检测到表中没有 ip:{} 的记录，现将自动添加，添加结果{}",
                    ip, result ? "成功" : "失败");
        }
        NodeStatus status = new NodeStatus();
        status.setId(info.getId());
        status.setNodeCode(info.getNodeCode());
        status.setDeployIp(info.getIp());
        status.setDetectionTime(new Date());
        status.setRegionCode(info.getRegionCode());
        status.setStatus(Status.NORMAL.code);
        nodeStatusService.reset(status);
        return info;
    }

    private void addNodeUsedInfo(NodeInfo nodeInfo, Server server) {
        NodeUsedInfo info = new NodeUsedInfo();
        info.setNodeCode(nodeInfo.getNodeCode());
        info.setHostname(nodeInfo.getHostname());
        info.setIp(nodeInfo.getIp());
        info.setDetectionTime(new Date());
        info.setRegionCode(nodeInfo.getRegionCode());
        NodeUsedInfo.convert(server, info);
        nodeUsedInfoService.save(info);
    }

    private void resetServiceStatus(List<ServerSoftwareStatus> statusList, Integer nodeCode, String ip) {
        statusList.forEach(s -> {
            String serviceName = s.getName();
            Integer checkPort = s.getCheckPort();
            String workDirectory = s.getWorkDirectory();
            ServiceInfo info = serviceInfoService.getOne(new LambdaQueryWrapper<ServiceInfo>()
                    .eq(ServiceInfo::getServiceName, serviceName)
                    .eq(ServiceInfo::getNodeCode, nodeCode)
                    .eq(ServiceInfo::getDeployIp, ip)
                    .eq(ServiceInfo::getPort, checkPort));
            if (info == null) {
                info = new ServiceInfo();
                info.setServiceCode(serviceInfoService.getServiceCode());
                info.setNodeCode(nodeCode);
                info.setServiceName(serviceName);
                info.setDeployIp(ip);
                info.setPort(checkPort);
                info.setHealthCheckUrl(HEALTH_CHECK_DEFAULT_URL);
                info.setStartupScriptPath(workDirectory + STARTUP_SCRIPT_DEFAULT_PATH);
                info.setStopScriptPath(workDirectory + STOP_SCRIPT_DEFAULT_PATH);
                info.setLogPath(workDirectory + LOG_DEFAULT_PATH);
                info.setEstimatedStartupTime(ESTIMATED_STARTUP_TIME_DEFAULT_SERVICE);
                info.setRegionCode(REGION_DEFAULT_CODE);
                info.setMonitorMode(MONITOR_MODE_ACTIVE);
                info.setSelfStart(Status.NO.code);
                info.setStatus(Status.YES.code);
                info.setRemark(StringUtil.format("nodeCode:{}({})主动发送监控数据", nodeCode, ip));
                boolean result = serviceInfoService.save(info);
                LOG.info("检测到表中没有 ip:{}, 服务名称:{}, 检测端口:{} 的记录，现将自动添加，添加结果{}",
                        ip, serviceName, checkPort, result ? "成功" : "失败");
            }
            ServiceStatus status = new ServiceStatus();
            status.setId(info.getId());
            status.setServiceCode(info.getServiceCode());
            status.setNodeCode(info.getNodeCode());
            status.setServiceName(info.getServiceName());
            status.setDeployIp(info.getDeployIp());
            status.setServiceStartupTime(s.getStartupTime());
            status.setDetectionTime(new Date());
            status.setRegionCode(info.getRegionCode());
            status.setStatus(s.getStatus() ? Status.NORMAL.code : Status.NOT_INITIATED.code);
            serviceStatusService.reset(status);
        });
    }

    private void resetSoftwareStatus(List<ServerSoftwareStatus> statusList, Integer nodeCode, String ip) {
        statusList.forEach(s -> {
            String softwareName = s.getName();
            Integer checkPort = s.getCheckPort();
            String processName = s.getProcessName();
            String workDirectory = SOFTWARE_SCRIPT_DEFAULT_ROOT_PATH + softwareName + "/" + processName;
            SoftwareInfo info = softwareInfoService.getOne(new LambdaQueryWrapper<SoftwareInfo>()
                    .eq(SoftwareInfo::getProcessName, processName)
                    .eq(SoftwareInfo::getNodeCode, nodeCode)
                    .eq(SoftwareInfo::getDeployIp, ip)
                    .eq(SoftwareInfo::getCheckPort, checkPort));

            if (info == null) {
                info = new SoftwareInfo();
                info.setSoftwareName(softwareName);
                info.setProcessName(processName);
                info.setNodeCode(nodeCode);
                info.setDeployIp(ip);
                info.setCheckPort(s.getCheckPort());
                info.setStartupScriptPath(workDirectory + STARTUP_SCRIPT_DEFAULT_PATH);
                info.setStopScriptPath(workDirectory + STOP_SCRIPT_DEFAULT_PATH);
                info.setEstimatedStartupTime(ESTIMATED_STARTUP_TIME_DEFAULT_SOFTWARE);
                info.setRegionCode(REGION_DEFAULT_CODE);
                info.setMonitorMode(MONITOR_MODE_ACTIVE);
                info.setSelfStart(Status.NO.code);
                info.setStatus(Status.YES.code);
                info.setRemark(StringUtil.format("nodeCode:{}({})主动发送监控数据", nodeCode, ip));
                boolean result = softwareInfoService.save(info);
                LOG.info("检测到表中没有 ip:{}, 进程名称:{}, 检测端口:{} 的记录，现将自动添加，添加结果{}",
                        ip, processName, checkPort, result ? "成功" : "失败");
            }
            SoftwareStatus status = new SoftwareStatus();
            status.setId(info.getId());
            status.setSoftwareName(info.getSoftwareName());
            status.setProcessName(info.getProcessName());
            status.setNodeCode(info.getNodeCode());
            status.setDeployIp(info.getDeployIp());
            status.setCheckPort(info.getCheckPort());
            status.setSoftwareStartupTime(s.getStartupTime());
            status.setDetectionTime(new Date());
            status.setRegionCode(info.getRegionCode());
            status.setStatus(s.getStatus() ? Status.NORMAL.code : Status.NOT_INITIATED.code);
            softwareStatusService.reset(status);
        });
    }
}
